package cn.net.sinodata.system.msghandler.process.base;

import cn.hutool.core.util.StrUtil;
import cn.net.sinodata.common.core.constant.CacheNames;
import cn.net.sinodata.common.core.enums.MsgParseHandlerTypeEnum;
import cn.net.sinodata.common.core.enums.MsgParseProcessStateEnum;
import cn.net.sinodata.common.core.enums.TaskTypeEnum;
import cn.net.sinodata.common.core.utils.SpringUtils;
import cn.net.sinodata.common.core.utils.TransactionUtil;
import cn.net.sinodata.common.hazelcast.utils.HazelcastUtils;
import cn.net.sinodata.common.json.utils.JsonUtils;
import cn.net.sinodata.system.domain.*;
import cn.net.sinodata.system.mapper.*;
import cn.net.sinodata.system.msghandler.exception.MsgParseException;
import cn.net.sinodata.system.msghandler.exception.NoRangeContainsException;
import cn.net.sinodata.system.msghandler.parse.MsgParseFactory;
import cn.net.sinodata.system.msghandler.parse.MsgParseHandler;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.Objects;

@Slf4j
public abstract class BaseMsgProcessHandler implements IMsgProcessHandler {

    // 存储数据
    protected BmTransLogRecord bmTransLogRecord;
    protected BmTransLogMsg bmTransLogMsg;
    // 用于存储一下公共的信息，如报文类型、业务类型、成交编号等。备注：成交编号的key必输是trans_serial
    public Map<String, Object> commonInfo = null;
    public static final String TRANS_SERIAL = "TransSerial";

    // 报文解析处理器
    protected MsgParseHandler msgParseHandler;

    // 对应的mapper
    public BmTransLogRecordMapper bmTransLogRecordMapper;
    public PmMonitorProductMapper pmMonitorProductMapper;
    public PmMsgTemplateMapper pmMsgTemplateMapper;
    public BmTransSerialMapper bmTransSerialMapper;
    public BmTransSubSerialMapper bmTransSubSerialMapper;

    public BaseMsgProcessHandler(BmTransLogRecord bmTransLogRecord, BmTransLogMsg bmTransLogMsg, MsgParseHandlerTypeEnum handlerType) {
        this.bmTransLogRecord = bmTransLogRecord;
        this.bmTransLogMsg = bmTransLogMsg;
        this.msgParseHandler = MsgParseFactory.build().getHandler(handlerType);

        bmTransLogRecordMapper = SpringUtils.getBean(BmTransLogRecordMapper.class);
        pmMonitorProductMapper = SpringUtils.getBean(PmMonitorProductMapper.class);
        pmMsgTemplateMapper = SpringUtils.getBean(PmMsgTemplateMapper.class);
        bmTransSerialMapper = SpringUtils.getBean(BmTransSerialMapper.class);
        bmTransSubSerialMapper = SpringUtils.getBean(BmTransSubSerialMapper.class);
    }

    @Override
    public void run() {
        String transLogRecordId = bmTransLogRecord.getId();
        log.info("报文解析-开始, transLogRecordId={}", transLogRecordId);

        TaskTypeEnum taskTypeEnum;
        PmMonitorProduct monitorProduct;

        // 基础逻辑判断，执行异常更新交易日志记录表
        try {
            // 获取公共信息
            this.commonInfo = getCommonInfo(bmTransLogRecord, bmTransLogMsg);
            if (StrUtil.isEmpty((String)commonInfo.get(TRANS_SERIAL))) {
                throw new Exception("成交编号为空");
            }

            // 判断任务类型
            taskTypeEnum = taskTypeHandle(bmTransLogRecord, bmTransLogMsg);
            if (taskTypeEnum == null) {
                log.warn("当前任务的类型为空");
                updateTransLogRecordProcState(MsgParseProcessStateEnum.NEEDLESS, "未识别出任务类型");
                return;
            }
            log.info("当前任务的类型为：{}", taskTypeEnum);

            // 查询产品信息
            monitorProduct = getMonitorProduct(bmTransLogRecord, bmTransLogMsg);
            if (Objects.isNull(monitorProduct)) {
                updateTransLogRecordProcState(MsgParseProcessStateEnum.NEEDLESS, "未识别出任务类型");
                log.warn("未查询到监控产品信息");
                return;
            }
        } catch (Exception e) {
            log.error("任务处理异常:", e);
            updateTransLogRecordProcState(MsgParseProcessStateEnum.EXCEPTION, "任务处理异常：" + e.getMessage());
            return;
        }

        // 匹配到监控产品后处理，执行异常添加流水表异常任务
        try {
            // 获取报文模板
            PmMsgTemplate msgTemplate = getPmMsgTemplate(taskTypeEnum, monitorProduct);
            if (Objects.isNull(msgTemplate)) {
                throw new Exception("未查询到报文模板信息");
            }
            String msgTemplateContent = msgTemplate.getMsgTemplateContent();
            ArrayNode msgTpl = (ArrayNode) JsonUtils.parseJsonNode(msgTemplateContent);

            //报文动态解析
            Object parseResult = msgParseHandler.parse(bmTransLogMsg.getResponseMsg());
            Map<Class, Object> entryMap = msgParseHandler.multiEntityMapper(msgTpl, parseResult);
            BmTransSerial bmTransSerial = (BmTransSerial)entryMap.get(BmTransSerial.class);
            BmTransSubSerial bmTransSubSerial = (BmTransSubSerial)entryMap.get(BmTransSubSerial.class);

            // 获取唯一性校验模板 TODO 需确认获取报文模板的逻辑上是不是公共的，如果不是公共的可以放在trans方法中
            String uniquenessRole = monitorProduct.getUniquenessRole();

            // 调用对应的执行方法
            switch (taskTypeEnum) {
                case TRANS -> trans(bmTransSerial, bmTransSubSerial);
                case CONFIRM -> confirm(bmTransSerial, bmTransSubSerial);
                case CLEARING -> clearing(bmTransSerial, bmTransSubSerial);
            }
        }catch (NoRangeContainsException e) {
            log.warn("本次解析报文不满足range要求：" + e.getMessage());
            updateTransLogRecordProcState(MsgParseProcessStateEnum.NEEDLESS, "未识别出任务类型");
        } catch (MsgParseException e) {
            log.error("报文解析失败：", e);
            addExpInTransSerial((String) commonInfo.get(TRANS_SERIAL), taskTypeEnum, "报文解析失败：" + e.getMessage());
        } catch (Exception e) {
            log.error("任务处理异常:", e);
            addExpInTransSerial((String) commonInfo.get(TRANS_SERIAL), taskTypeEnum, "任务处理异常：" + e.getMessage());
        }
        log.info("报文解析-结束, id={}", transLogRecordId);
    }

    /**
     *
     * @param bmTransLogRecord
     * @param bmTransLogMsg
     * @return
     * @throws Exception
     */
    public abstract Map<String, Object> getCommonInfo(BmTransLogRecord bmTransLogRecord, BmTransLogMsg bmTransLogMsg) throws Exception;

    @Override
    public abstract TaskTypeEnum taskTypeHandle(BmTransLogRecord bmTransLogRecord, BmTransLogMsg bmTransLogMsg) throws Exception;

    @Override
    public abstract PmMonitorProduct getMonitorProduct(BmTransLogRecord bmTransLogRecord, BmTransLogMsg bmTransLogMsg) throws Exception;

    @Override
    public abstract void trans(BmTransSerial bmTransSerial, BmTransSubSerial bmTransSubSerial) throws Exception;

    @Override
    public abstract void confirm(BmTransSerial bmTransSerial, BmTransSubSerial bmTransSubSerial) throws Exception;

    @Override
    public abstract void clearing(BmTransSerial bmTransSerial, BmTransSubSerial bmTransSubSerial) throws Exception;

    /**
     * 查询监控产品信息
     * @param taskTypeEnum
     * @param monitorProduct
     * @return
     * @throws Exception
     */
    private PmMsgTemplate getPmMsgTemplate(TaskTypeEnum taskTypeEnum, PmMonitorProduct monitorProduct) throws Exception {
        // 调用对应的执行方法
        String msgTplCode = switch (taskTypeEnum) {
            case TRANS -> monitorProduct.getMessageTemp();
            case CONFIRM -> monitorProduct.getMessageTemp(); // TODO: 更改为对应的字段
            case CLEARING -> monitorProduct.getMessageTemp(); // TODO: 更改为对应的字段
        };
        PmMsgTemplate msgTemplate = HazelcastUtils.getCache(CacheNames.PM_MSG_TEMPLATE, msgTplCode);
        return msgTemplate;
    }

    /**
     * 更新交易日志记录表的状态
     * @param processStateEnum 流程状态
     * @param stateComment 状态描述
     */
    public void updateTransLogRecordProcState(MsgParseProcessStateEnum processStateEnum, String stateComment) {
        log.info("更新交易日志记录表的状态-开始 id={}， 状态：{}", bmTransLogRecord.getId(), processStateEnum.code);
        LambdaUpdateWrapper<BmTransLogRecord> luw = new LambdaUpdateWrapper<BmTransLogRecord>()
            .set(BmTransLogRecord::getProcessState, processStateEnum.code)
            .set(StrUtil.isNotEmpty(stateComment), BmTransLogRecord::getStateComment, stateComment)
            .eq(BmTransLogRecord::getId, bmTransLogRecord.getId());
        bmTransLogRecordMapper.update(luw);
        log.info("更新交易日志记录表的状态-结束");
    }

    /**
     * 添加交易流水表异常任务
     * @param transSerial
     * @param taskTypeEnum
     * @param abnormalReason
     */
    public void addExpInTransSerial(String transSerial, TaskTypeEnum taskTypeEnum, String abnormalReason) {
        log.info("添加交易流水表异常任务-开始 成交编号={}，任务类型：{}，交易日志记录id={}", transSerial, taskTypeEnum.code, bmTransLogRecord.getId());

        BmTransSerial bmTransSerial = new BmTransSerial();
        bmTransSerial.setTransNo(transSerial);
        bmTransSerial.setTransSerial(bmTransLogRecord.getTransSerial());
        switch (taskTypeEnum) {
            case TRANS -> bmTransSerial.setTransMessageid(bmTransLogRecord.getId());
            case CONFIRM -> bmTransSerial.setTransConfirmMessageId(bmTransLogRecord.getId());
            case CLEARING -> bmTransSerial.setTransClearingMessageId(bmTransLogRecord.getId());
        }
        bmTransSerial.setIsAbnormal("1");
        // abnormalReason字段处理
        String abnormalReasonTmp = abnormalReason;
        if (StrUtil.isNotEmpty(abnormalReasonTmp) && abnormalReasonTmp.length() > 256) {
            abnormalReasonTmp = abnormalReasonTmp.substring(abnormalReasonTmp.length()-256, abnormalReasonTmp.length());
        }
        bmTransSerial.setAbnormalReason(abnormalReasonTmp);

        // 手动开启事务进行更新
        TransactionUtil.executeWithoutResult(() -> {
            // 将交易流水表状态更新成完成
            updateTransLogRecordProcState(MsgParseProcessStateEnum.COMPLETED, "任务进入到流水表，异常状态");
            // 入交易流水异常
            bmTransSerialMapper.insert(bmTransSerial);
        });
        log.info("添加交易流水表异常任务-结束");
    }
}
